summaryrefslogtreecommitdiff
path: root/app/[lng]
diff options
context:
space:
mode:
Diffstat (limited to 'app/[lng]')
-rw-r--r--app/[lng]/admin/ecc/page.tsx474
-rw-r--r--app/[lng]/admin/mdg/page.tsx26
2 files changed, 378 insertions, 122 deletions
diff --git a/app/[lng]/admin/ecc/page.tsx b/app/[lng]/admin/ecc/page.tsx
index 257ecfea..cd09e213 100644
--- a/app/[lng]/admin/ecc/page.tsx
+++ b/app/[lng]/admin/ecc/page.tsx
@@ -10,7 +10,7 @@ import { Label } from '@/components/ui/label'
import { Badge } from '@/components/ui/badge'
import { Separator } from '@/components/ui/separator'
import { Alert, AlertDescription } from '@/components/ui/alert'
-import { Loader2, Play, CheckCircle, XCircle, Send } from 'lucide-react'
+import { Loader2, Play, CheckCircle, XCircle, Send, Plus, Minus } from 'lucide-react'
import { toast } from 'sonner'
// SOAP 송신 함수들 import
@@ -125,36 +125,96 @@ export default function ECCSenderTestPage() {
// PO 생성 테스트
const [poData, setPoData] = useState({
- // 헤더 정보
- ANFNR: 'TEST001',
- LIFNR: '1000000001',
- ZPROC_IND: 'A',
- ANGNR: 'TEST001',
- WAERS: 'KRW',
- ZTERM: '0001',
- INCO1: 'FOB',
- INCO2: 'Seoul, Korea',
- MWSKZ: 'V0',
- LANDS: 'KR',
- ZRCV_DT: '20241201',
- ZATTEN_IND: 'Y',
- IHRAN: '20241201',
- TEXT: 'Test PO Creation',
- // 아이템 정보
- ANFPS: '00001',
- NETPR: '1000.00',
- PEINH: '1',
- BPRME: 'EA',
- NETWR: '1000.00',
- BRTWR: '1100.00',
- LFDAT: '20241201',
- // PR 반환 정보
- EBELN: 'PR001',
- EBELP: '00001',
- MSGTY: 'S',
- MSGTXT: 'Test message'
+ header: {
+ ANFNR: 'TEST001',
+ LIFNR: '1000000001',
+ ZPROC_IND: 'A',
+ ANGNR: 'TEST001',
+ WAERS: 'KRW',
+ ZTERM: '0001',
+ INCO1: 'FOB',
+ INCO2: 'Seoul, Korea',
+ MWSKZ: 'V0',
+ LANDS: 'KR',
+ ZRCV_DT: '20241201',
+ ZATTEN_IND: 'Y',
+ IHRAN: '20241201',
+ TEXT: 'Test PO Creation',
+ LSTEL: '',
+ VSTEL: '',
+ ZDLV_CNTLR: '',
+ ZDLV_PRICE_NOTE: '',
+ ZDLV_PRICE_T: ''
+ },
+ items: [{
+ ANFNR: 'TEST001',
+ ANFPS: '00001',
+ LIFNR: '1000000001',
+ NETPR: '1000.00',
+ PEINH: '1',
+ BPRME: 'EA',
+ NETWR: '1000.00',
+ BRTWR: '1100.00',
+ LFDAT: '20241201',
+ EBELP: '',
+ ZCON_NO_PO: ''
+ }],
+ prReturn: [{
+ ANFNR: 'TEST001',
+ ANFPS: '00001',
+ EBELN: 'PR001',
+ EBELP: '00001',
+ MSGTY: 'S',
+ MSGTXT: 'Test message'
+ }]
})
+ // PO 아이템 관리 함수들
+ const addPoItem = () => {
+ const newItemIndex = poData.items.length + 1
+ setPoData(prev => ({
+ ...prev,
+ items: [...prev.items, {
+ ANFNR: prev.header.ANFNR,
+ ANFPS: String(newItemIndex).padStart(5, '0'),
+ LIFNR: prev.header.LIFNR,
+ NETPR: '0.00',
+ PEINH: '1',
+ BPRME: 'EA',
+ NETWR: '0.00',
+ BRTWR: '0.00',
+ LFDAT: prev.header.IHRAN,
+ EBELP: '',
+ ZCON_NO_PO: ''
+ }]
+ }))
+ }
+
+ const removePoItem = (index: number) => {
+ if (poData.items.length > 1) {
+ setPoData(prev => ({
+ ...prev,
+ items: prev.items.filter((_, i) => i !== index)
+ }))
+ }
+ }
+
+ const updatePoItem = (index: number, field: string, value: string) => {
+ setPoData(prev => ({
+ ...prev,
+ items: prev.items.map((item, i) =>
+ i === index ? { ...item, [field]: value } : item
+ )
+ }))
+ }
+
+ const updatePoHeader = (field: string, value: string) => {
+ setPoData(prev => ({
+ ...prev,
+ header: { ...prev.header, [field]: value }
+ }))
+ }
+
return (
<div className="container mx-auto py-8 space-y-6">
<div className="flex items-center justify-between">
@@ -513,87 +573,188 @@ export default function ECCSenderTestPage() {
PO (Purchase Order) 생성
</CardTitle>
<CardDescription>
- 구매주문 생성 요청을 ECC로 전송합니다. (IF_ECC_EVCP_PO_CREATE)
+ 구매주문 생성 요청을 ECC로 전송합니다. (IF_ECC_EVCP_PO_CREATE) - 하나의 PO에 여러 PR 아이템들을 포함시키는 구조
</CardDescription>
</CardHeader>
- <CardContent className="space-y-4">
+ <CardContent className="space-y-6">
+ {/* PO 헤더 정보 */}
<div className="space-y-4">
- <h4 className="font-semibold">PO 헤더 정보</h4>
- <div className="grid grid-cols-2 gap-4">
+ <div className="flex items-center justify-between">
+ <h4 className="font-semibold">PO 헤더 정보</h4>
+ <Badge variant="outline" className="text-xs">
+ {poData.items.length}개 아이템
+ </Badge>
+ </div>
+ <div className="grid grid-cols-3 gap-4">
<div className="space-y-2">
- <Label htmlFor="po-anfnr">입찰번호 (필수)</Label>
+ <Label htmlFor="po-anfnr">입찰번호 (ANFNR) <span className="text-red-500">*</span></Label>
<Input
id="po-anfnr"
- value={poData.ANFNR}
- onChange={(e) => setPoData(prev => ({ ...prev, ANFNR: e.target.value }))}
- placeholder="입찰번호"
+ value={poData.header.ANFNR}
+ onChange={(e) => updatePoHeader('ANFNR', e.target.value)}
+ placeholder="입찰번호 (ANFNR)"
/>
</div>
<div className="space-y-2">
- <Label htmlFor="po-lifnr">공급업체 계정 (필수)</Label>
+ <Label htmlFor="po-lifnr">공급업체 계정 (LIFNR) : 벤더코드 <span className="text-red-500">*</span></Label>
<Input
id="po-lifnr"
- value={poData.LIFNR}
- onChange={(e) => setPoData(prev => ({ ...prev, LIFNR: e.target.value }))}
- placeholder="공급업체 계정번호"
+ value={poData.header.LIFNR}
+ onChange={(e) => updatePoHeader('LIFNR', e.target.value)}
+ placeholder="공급업체 계정 (LIFNR) : 벤더코드"
/>
</div>
<div className="space-y-2">
- <Label htmlFor="po-zproc">처리상태 (필수)</Label>
+ <Label htmlFor="po-zproc">처리상태 (ZPROC_IND) <span className="text-red-500">*</span></Label>
<Input
id="po-zproc"
- value={poData.ZPROC_IND}
- onChange={(e) => setPoData(prev => ({ ...prev, ZPROC_IND: e.target.value }))}
- placeholder="구매처리상태"
+ value={poData.header.ZPROC_IND}
+ onChange={(e) => updatePoHeader('ZPROC_IND', e.target.value)}
+ placeholder="처리상태 (ZPROC_IND)"
/>
</div>
<div className="space-y-2">
- <Label htmlFor="po-waers">통화 (필수)</Label>
+ <Label htmlFor="po-angnr">협상번호 (ANGNR)</Label>
+ <Input
+ id="po-angnr"
+ value={poData.header.ANGNR}
+ onChange={(e) => updatePoHeader('ANGNR', e.target.value)}
+ placeholder="협상번호 (ANGNR)"
+ />
+ </div>
+ <div className="space-y-2">
+ <Label htmlFor="po-waers">통화 (WAERS) <span className="text-red-500">*</span></Label>
<Input
id="po-waers"
- value={poData.WAERS}
- onChange={(e) => setPoData(prev => ({ ...prev, WAERS: e.target.value }))}
- placeholder="통화 코드"
+ value={poData.header.WAERS}
+ onChange={(e) => updatePoHeader('WAERS', e.target.value)}
+ placeholder="통화 (WAERS)"
/>
</div>
- </div>
-
- <h4 className="font-semibold">PO 아이템 정보</h4>
- <div className="grid grid-cols-2 gap-4">
<div className="space-y-2">
- <Label htmlFor="po-anfps">입찰 아이템번호 (필수)</Label>
+ <Label htmlFor="po-zterm">지급조건 (ZTERM) <span className="text-red-500">*</span></Label>
<Input
- id="po-anfps"
- value={poData.ANFPS}
- onChange={(e) => setPoData(prev => ({ ...prev, ANFPS: e.target.value }))}
- placeholder="입찰 아이템번호"
+ id="po-zterm"
+ value={poData.header.ZTERM}
+ onChange={(e) => updatePoHeader('ZTERM', e.target.value)}
+ placeholder="지급조건 (ZTERM)"
/>
</div>
<div className="space-y-2">
- <Label htmlFor="po-netpr">순가격 (필수)</Label>
+ <Label htmlFor="po-inco1">인코텀즈1 (INCO1) <span className="text-red-500">*</span></Label>
<Input
- id="po-netpr"
- value={poData.NETPR}
- onChange={(e) => setPoData(prev => ({ ...prev, NETPR: e.target.value }))}
- placeholder="순가격"
+ id="po-inco1"
+ value={poData.header.INCO1}
+ onChange={(e) => updatePoHeader('INCO1', e.target.value)}
+ placeholder="인코텀즈1 (INCO1)"
+ />
+ </div>
+ <div className="space-y-2">
+ <Label htmlFor="po-inco2">인코텀즈2 (INCO2) <span className="text-red-500">*</span></Label>
+ <Input
+ id="po-inco2"
+ value={poData.header.INCO2}
+ onChange={(e) => updatePoHeader('INCO2', e.target.value)}
+ placeholder="인코텀즈2 (INCO2)"
+ />
+ </div>
+ <div className="space-y-2">
+ <Label htmlFor="po-mwskz">세금코드 (MWSKZ) <span className="text-red-500">*</span></Label>
+ <Input
+ id="po-mwskz"
+ value={poData.header.MWSKZ}
+ onChange={(e) => updatePoHeader('MWSKZ', e.target.value)}
+ placeholder="세금코드 (MWSKZ)"
/>
</div>
<div className="space-y-2">
- <Label htmlFor="po-bprme">주문단위 (필수)</Label>
+ <Label htmlFor="po-lands">국가키 (LANDS) <span className="text-red-500">*</span></Label>
<Input
- id="po-bprme"
- value={poData.BPRME}
- onChange={(e) => setPoData(prev => ({ ...prev, BPRME: e.target.value }))}
- placeholder="주문단위 (예: EA)"
+ id="po-lands"
+ value={poData.header.LANDS}
+ onChange={(e) => updatePoHeader('LANDS', e.target.value)}
+ placeholder="국가키 (LANDS)"
/>
</div>
<div className="space-y-2">
- <Label htmlFor="po-lfdat">납기일 (필수)</Label>
+ <Label htmlFor="po-zrcv-dt">수령일 (ZRCV_DT) <span className="text-red-500">*</span></Label>
<Input
- id="po-lfdat"
- value={poData.LFDAT}
- onChange={(e) => setPoData(prev => ({ ...prev, LFDAT: e.target.value }))}
- placeholder="YYYYMMDD 형식"
+ id="po-zrcv-dt"
+ value={poData.header.ZRCV_DT}
+ onChange={(e) => updatePoHeader('ZRCV_DT', e.target.value)}
+ placeholder="수령일 (ZRCV_DT)"
+ />
+ </div>
+ <div className="space-y-2">
+ <Label htmlFor="po-zatten-ind">참석지시자 (ZATTEN_IND) <span className="text-red-500">*</span></Label>
+ <Input
+ id="po-zatten-ind"
+ value={poData.header.ZATTEN_IND}
+ onChange={(e) => updatePoHeader('ZATTEN_IND', e.target.value)}
+ placeholder="참석지시자 (ZATTEN_IND)"
+ />
+ </div>
+ <div className="space-y-2">
+ <Label htmlFor="po-ihran">입찰마감일 (IHRAN) <span className="text-red-500">*</span></Label>
+ <Input
+ id="po-ihran"
+ value={poData.header.IHRAN}
+ onChange={(e) => updatePoHeader('IHRAN', e.target.value)}
+ placeholder="입찰마감일 (IHRAN)"
+ />
+ </div>
+ <div className="space-y-2">
+ <Label htmlFor="po-text">텍스트 (TEXT)</Label>
+ <Input
+ id="po-text"
+ value={poData.header.TEXT}
+ onChange={(e) => updatePoHeader('TEXT', e.target.value)}
+ placeholder="텍스트 (TEXT)"
+ />
+ </div>
+ <div className="space-y-2">
+ <Label htmlFor="po-lstel">배송지점 (LSTEL)</Label>
+ <Input
+ id="po-lstel"
+ value={poData.header.LSTEL}
+ onChange={(e) => updatePoHeader('LSTEL', e.target.value)}
+ placeholder="배송지점 (LSTEL)"
+ />
+ </div>
+ <div className="space-y-2">
+ <Label htmlFor="po-vstel">출하지점 (VSTEL)</Label>
+ <Input
+ id="po-vstel"
+ value={poData.header.VSTEL}
+ onChange={(e) => updatePoHeader('VSTEL', e.target.value)}
+ placeholder="출하지점 (VSTEL)"
+ />
+ </div>
+ <div className="space-y-2">
+ <Label htmlFor="po-zdlv-cntl">납품담당자 (ZDLV_CNTLR)</Label>
+ <Input
+ id="po-zdlv-cntl"
+ value={poData.header.ZDLV_CNTLR}
+ onChange={(e) => updatePoHeader('ZDLV_CNTLR', e.target.value)}
+ placeholder="납품담당자 (ZDLV_CNTLR)"
+ />
+ </div>
+ <div className="space-y-2">
+ <Label htmlFor="po-zdlv-price-note">가격노트 (ZDLV_PRICE_NOTE)</Label>
+ <Input
+ id="po-zdlv-price-note"
+ value={poData.header.ZDLV_PRICE_NOTE}
+ onChange={(e) => updatePoHeader('ZDLV_PRICE_NOTE', e.target.value)}
+ placeholder="가격노트 (ZDLV_PRICE_NOTE)"
+ />
+ </div>
+ <div className="space-y-2">
+ <Label htmlFor="po-zdlv-price-t">가격유형 (ZDLV_PRICE_T)</Label>
+ <Input
+ id="po-zdlv-price-t"
+ value={poData.header.ZDLV_PRICE_T}
+ onChange={(e) => updatePoHeader('ZDLV_PRICE_T', e.target.value)}
+ placeholder="가격유형 (ZDLV_PRICE_T)"
/>
</div>
</div>
@@ -601,6 +762,133 @@ export default function ECCSenderTestPage() {
<Separator />
+ {/* PO 아이템 정보 */}
+ <div className="space-y-4">
+ <div className="flex items-center justify-between">
+ <h4 className="font-semibold">PO 아이템 정보</h4>
+ <Button
+ onClick={addPoItem}
+ size="sm"
+ variant="outline"
+ className="flex items-center gap-1"
+ >
+ <Plus className="h-4 w-4" />
+ 아이템 추가
+ </Button>
+ </div>
+
+ {poData.items.map((item, index) => (
+ <Card key={index} className="p-4">
+ <div className="flex items-center justify-between mb-4">
+ <h5 className="font-medium">아이템 #{index + 1}</h5>
+ {poData.items.length > 1 && (
+ <Button
+ onClick={() => removePoItem(index)}
+ size="sm"
+ variant="destructive"
+ className="flex items-center gap-1"
+ >
+ <Minus className="h-4 w-4" />
+ 삭제
+ </Button>
+ )}
+ </div>
+ <div className="grid grid-cols-3 gap-4">
+ <div className="space-y-2">
+ <Label>입찰번호 (ANFNR) <span className="text-red-500">*</span></Label>
+ <Input
+ value={item.ANFNR}
+ onChange={(e) => updatePoItem(index, 'ANFNR', e.target.value)}
+ placeholder="입찰번호 (ANFNR)"
+ />
+ </div>
+ <div className="space-y-2">
+ <Label>입찰 아이템번호 (ANFPS) <span className="text-red-500">*</span></Label>
+ <Input
+ value={item.ANFPS}
+ onChange={(e) => updatePoItem(index, 'ANFPS', e.target.value)}
+ placeholder="입찰 아이템번호 (ANFPS)"
+ />
+ </div>
+ <div className="space-y-2">
+ <Label>공급업체 계정 (LIFNR) : 벤더코드 <span className="text-red-500">*</span></Label>
+ <Input
+ value={item.LIFNR}
+ onChange={(e) => updatePoItem(index, 'LIFNR', e.target.value)}
+ placeholder="공급업체 계정 (LIFNR) : 벤더코드"
+ />
+ </div>
+ <div className="space-y-2">
+ <Label>순가격 (NETPR) <span className="text-red-500">*</span></Label>
+ <Input
+ value={item.NETPR}
+ onChange={(e) => updatePoItem(index, 'NETPR', e.target.value)}
+ placeholder="순가격 (NETPR)"
+ />
+ </div>
+ <div className="space-y-2">
+ <Label>가격단위 (PEINH) <span className="text-red-500">*</span></Label>
+ <Input
+ value={item.PEINH}
+ onChange={(e) => updatePoItem(index, 'PEINH', e.target.value)}
+ placeholder="가격단위 (PEINH)"
+ />
+ </div>
+ <div className="space-y-2">
+ <Label>주문단위 (BPRME) <span className="text-red-500">*</span></Label>
+ <Input
+ value={item.BPRME}
+ onChange={(e) => updatePoItem(index, 'BPRME', e.target.value)}
+ placeholder="주문단위 (BPRME)"
+ />
+ </div>
+ <div className="space-y-2">
+ <Label>순금액 (NETWR) <span className="text-red-500">*</span></Label>
+ <Input
+ value={item.NETWR}
+ onChange={(e) => updatePoItem(index, 'NETWR', e.target.value)}
+ placeholder="순금액 (NETWR)"
+ />
+ </div>
+ <div className="space-y-2">
+ <Label>총금액 (BRTWR) <span className="text-red-500">*</span></Label>
+ <Input
+ value={item.BRTWR}
+ onChange={(e) => updatePoItem(index, 'BRTWR', e.target.value)}
+ placeholder="총금액 (BRTWR)"
+ />
+ </div>
+ <div className="space-y-2">
+ <Label>납기일 (LFDAT) <span className="text-red-500">*</span></Label>
+ <Input
+ value={item.LFDAT}
+ onChange={(e) => updatePoItem(index, 'LFDAT', e.target.value)}
+ placeholder="납기일 (LFDAT)"
+ />
+ </div>
+ <div className="space-y-2">
+ <Label>구매오더 품번 (EBELP)</Label>
+ <Input
+ value={item.EBELP}
+ onChange={(e) => updatePoItem(index, 'EBELP', e.target.value)}
+ placeholder="구매오더 품번 (EBELP)"
+ />
+ </div>
+ <div className="space-y-2">
+ <Label>계약번호 (ZCON_NO_PO)</Label>
+ <Input
+ value={item.ZCON_NO_PO}
+ onChange={(e) => updatePoItem(index, 'ZCON_NO_PO', e.target.value)}
+ placeholder="계약번호 (ZCON_NO_PO)"
+ />
+ </div>
+ </div>
+ </Card>
+ ))}
+ </div>
+
+ <Separator />
+
<div className="flex gap-4">
<Button
onClick={() => runTest('PO 생성 (샘플)', () => createTestPurchaseOrder())}
@@ -614,41 +902,9 @@ export default function ECCSenderTestPage() {
<Button
onClick={() => runTest('PO 생성 (사용자)', async () => {
const poRequest = {
- T_Bidding_HEADER: [{
- ANFNR: poData.ANFNR,
- LIFNR: poData.LIFNR,
- ZPROC_IND: poData.ZPROC_IND,
- ANGNR: poData.ANGNR,
- WAERS: poData.WAERS,
- ZTERM: poData.ZTERM,
- INCO1: poData.INCO1,
- INCO2: poData.INCO2,
- MWSKZ: poData.MWSKZ,
- LANDS: poData.LANDS,
- ZRCV_DT: poData.ZRCV_DT,
- ZATTEN_IND: poData.ZATTEN_IND,
- IHRAN: poData.IHRAN,
- TEXT: poData.TEXT
- }],
- T_Bidding_ITEM: [{
- ANFNR: poData.ANFNR,
- ANFPS: poData.ANFPS,
- LIFNR: poData.LIFNR,
- NETPR: poData.NETPR,
- PEINH: poData.PEINH,
- BPRME: poData.BPRME,
- NETWR: poData.NETWR,
- BRTWR: poData.BRTWR,
- LFDAT: poData.LFDAT
- }],
- T_PR_RETURN: [{
- ANFNR: poData.ANFNR,
- ANFPS: poData.ANFPS,
- EBELN: poData.EBELN,
- EBELP: poData.EBELP,
- MSGTY: poData.MSGTY,
- MSGTXT: poData.MSGTXT
- }]
+ T_Bidding_HEADER: [poData.header],
+ T_Bidding_ITEM: poData.items,
+ T_PR_RETURN: poData.prReturn
}
return createPurchaseOrder(poRequest)
})}
@@ -656,7 +912,7 @@ export default function ECCSenderTestPage() {
>
{isLoading['PO 생성 (사용자)'] && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
<Send className="mr-2 h-4 w-4" />
- 사용자 데이터로 생성
+ 사용자 데이터로 생성 ({poData.items.length}개 아이템)
</Button>
</div>
diff --git a/app/[lng]/admin/mdg/page.tsx b/app/[lng]/admin/mdg/page.tsx
index 1bf37667..1f4a1ee8 100644
--- a/app/[lng]/admin/mdg/page.tsx
+++ b/app/[lng]/admin/mdg/page.tsx
@@ -34,34 +34,34 @@ const parseCSV = (csv: string): VendorFieldDef[] => {
// 기존 샘플 기본값 (필요 시 확장)
const sampleDefaults: Record<string, string> = {
- BP_HEADER: 'TEST001',
+ BP_HEADER: 'TEST002',
ZZSRMCD: 'EVCP',
- SORT1: 'TEST VENDOR',
+ SORT1: '테스트 벤더 회사',
NAME1: '테스트 벤더 회사',
- KTOKK: 'Z001',
+ KTOKK: 'LIEF',
J_1KFTBUS: '제조업',
- J_1KFTIND: 'IT',
- MASTERFLAG: 'X',
- IBND_TYPE: 'U',
- ZZREQID: 'TESTUSER01',
+ J_1KFTIND: '건설업외',
+ MASTERFLAG: 'V',
+ IBND_TYPE: 'I',
+ ZZREQID: 'HUIN84.KIM',
ADDRNO: '',
NATION: '',
COUNTRY: 'KR',
- LANGU: 'K',
+ LANGU: '',
POST_CODE1: '06292',
CITY1: '서울시',
CITY2: '강남구',
- REGION: '11',
+ REGION: '',
STREET: '테헤란로 123',
TEL_NUMBER: '02-1234-5678',
FAX_NUMBER: '02-1234-5679',
URI_ADDR: 'https://test.vendor.com',
SMTP_ADDR: 'contact@test.vendor.com',
- TAXTYPE: 'KR2',
- TAXNUM: '123-45-67890',
+ TAXTYPE: '9501010000000',
+ TAXNUM: '1234567890',
CONSNUMBER: '1',
- BP_TX_TYP: 'KR2',
- R3_USER: 'Y',
+ BP_TX_TYP: '9501010000000',
+ R3_USER: '0',
};
export default function MDGTestPage() {